using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace VSSDKAssist.Recipes.Common.Helpers
{
    public enum IconSize { Large, Small, Shell };

    public static class ImagelistHelper
    {
        private const uint SHGFI_ICON = 0x100;
        private const uint SHGFI_LINKOVERLAY = 0x8000;
        private const uint SHGFI_SELECTED = 0x10000;
        private const uint SHGFI_LARGEICON = 0x0;
        private const uint SHGFI_SMALLICON = 0x1;
        private const uint SHGFI_OPENICON = 0x2;
        private const uint SHGFI_SHELLICONSIZE = 0x4;
        private const uint SHGFI_USEFILEATTRIBUTES = 0x10;
        private const uint SHGFI_TYPENAME = 0x400;

        [StructLayout(LayoutKind.Sequential)]
        private struct SHFILEINFO
        {
            public IntPtr hIcon;
            public IntPtr iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        };

        [StructLayout(LayoutKind.Sequential)]
        private class BITMAPINFO
        {
            public Int32 biSize;
            public Int32 biWidth;
            public Int32 biHeight;
            public Int16 biPlanes;
            public Int16 biBitCount;
            public Int32 biCompression;
            public Int32 biSizeImage;
            public Int32 biXPelsPerMeter;
            public Int32 biYPelsPerMeter;
            public Int32 biClrUsed;
            public Int32 biClrImportant;
            public Int32 colors;
        };

        [DllImport("comctl32.dll")]
        private static extern bool ImageList_Add(IntPtr hImageList, IntPtr hBitmap, IntPtr hMask);
        [DllImport("kernel32.dll")]
        private static extern bool RtlMoveMemory(IntPtr dest, IntPtr source, int dwcount);
        [DllImport("shell32.dll")]
        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
        [DllImport("user32.dll")]
        private static extern IntPtr DestroyIcon(IntPtr hIcon);
        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateDIBSection(IntPtr hdc, [In, MarshalAs(UnmanagedType.LPStruct)]BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

        public static void AddFromIconFile(string iconFileName, ImageList destinationImagelist)
        {
            Bitmap bm = new Bitmap(iconFileName);

            if(bm.RawFormat.Guid == ImageFormat.Icon.Guid)
            {
                Icon icn = new Icon(iconFileName);
                destinationImagelist.Images.Add(Icon.FromHandle(icn.Handle));
                icn.Dispose();
            }
            else
            {
                Add(bm, destinationImagelist);
            }
            bm.Dispose();
        }

        private static void Add(string pszPath, ImageList il, uint uFlags)
        {
            SHFILEINFO SHInfo = new SHFILEINFO();

            IntPtr hImg = SHGetFileInfo(pszPath, 0, ref SHInfo, (uint)Marshal.SizeOf(SHInfo), SHGFI_ICON | uFlags);
            il.Images.Add(Icon.FromHandle(SHInfo.hIcon));
            DestroyIcon(SHInfo.hIcon);
        }

        private static void Add(Bitmap bm, ImageList il)
        {
            IntPtr hBitmap, ppvBits;
            BITMAPINFO bmi = new BITMAPINFO();

            if(bm.Size != il.ImageSize)
            {
                bm = new Bitmap(bm, il.ImageSize.Width, il.ImageSize.Height);
            }

            bmi.biSize = 40;
            bmi.biBitCount = 32;
            bmi.biPlanes = 1;
            bmi.biWidth = bm.Width;
            bmi.biHeight = bm.Height;
            bm.RotateFlip(RotateFlipType.RotateNoneFlipY);
            hBitmap = CreateDIBSection(new IntPtr(0), bmi, 0, out ppvBits, new IntPtr(0), 0);
            BitmapData bitmapData = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            RtlMoveMemory(ppvBits, bitmapData.Scan0, bm.Height * bitmapData.Stride);
            bm.UnlockBits(bitmapData);
            ImageList_Add(il.Handle, hBitmap, new IntPtr(0));
        }
    }
}